package org.btik.light.mobile.meta;

import android.util.Log;
import org.btik.context.SimpleContext;
import org.btik.light.meta.exception.LightException;
import org.btik.light.mobile.meta.mo.BaseMoCreator;
import org.btik.light.mobile.mo.DevMoFactory;
import org.btik.light.mobile.mo.MoCreator;
import org.btik.light.mobile.ui.factory.DevPanel;
import org.btik.light.mobile.ui.factory.DevPanelActivity;
import org.btik.light.mobile.ui.factory.DevPanelFactory;
import org.btik.light.mobile.ui.factory.SerialControllerPanel;
import org.btik.light.tool.xml.DomUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;

import static org.btik.light.mobile.meta.DevMetaXmlConstant.*;
import static org.btik.light.tool.xml.DomUtil.eachByTagName;
import static org.btik.light.tool.xml.DomUtil.getFirstByTagName;

/**
 * @author lustre
 * @since 2022/12/20 16:58
 */
public class MetaFactory {
    private final DevMoFactory devMoFactory;

    public MetaFactory(DevMoFactory devMoFactory) {
        this.devMoFactory = devMoFactory;
    }

    public void load() {
        try (InputStream resourceAsStream = MetaFactory.class.getResourceAsStream(CONTEXT_FILE_NAME)) {
            Document document = DomUtil.parse(resourceAsStream);
            Element rootElement = document.getDocumentElement();
            if (!ROOT_TAG.equals(rootElement.getTagName())) {

                throw new LightException("root tag not match :" + rootElement.getTagName());
            }
            parse(rootElement);
        } catch (IOException | ParserConfigurationException | SAXException | LightException e) {
            e.printStackTrace();
            Log.d("error", "load: meta failed");
        }

    }

    private void parse(Element rootElement) {
        parseDevPanel(rootElement);
        parseSerialController(rootElement);
    }

    private void parseSerialController(Element rootElement) {
        eachByTagName(rootElement, SERIAL_CONTROLLER, (serialController) ->{
            String type = serialController.getAttribute(TYPE);
            String clazz = serialController.getAttribute(CLASS);
            try {
                Class<?> aClass = Class.forName(clazz);
                if(!SerialControllerPanel.class.isAssignableFrom(aClass)){
                    throw new LightException("is not a SerialControllerPanel class");
                }
                DevPanelFactory.regSerialControllerPanel(type, clazz);
            } catch (ClassNotFoundException e) {
                throw new LightException(e);
            }
        });
    }

    private void parseDevPanel(Element rootElement) {
        eachByTagName(rootElement, DEV_META, (devMeta) -> {
            String devType = devMeta.getAttribute(DEV_TYPE);
            Element moCreator = getFirstByTagName(devMeta, MO_CREATOR);
            MoCreator moCreatorBean;
            if (moCreator != null) {
                moCreatorBean = parseMoCreator(moCreator);
            } else {
                moCreator = getFirstByTagName(devMeta, BASE_MO_CREATOR);
                moCreatorBean = newBaseMoCreator(moCreator);
            }
            Class<?> devPanelType = parseDevPanelType(getFirstByTagName(devMeta, DEV_PANEL));

            if (DevPanelActivity.class.isAssignableFrom(devPanelType)) {
                DevPanelFactory.regDevPanelActMeta(devType, (Class<? extends DevPanelActivity>) devPanelType);

            } else if (DevPanel.class.isAssignableFrom(devPanelType)) {
                DevPanelFactory.regDevPanelMeta(devType, (Class<? extends DevPanel<?>>) devPanelType);
            } else {
                throw new LightException(devPanelType + "is not a devPanel type");
            }

            DefaultMeta defaultMeta = new DefaultMeta(moCreatorBean, devPanelType);
            SimpleContext.set(devType, defaultMeta);
            devMoFactory.regCreator(devType, moCreatorBean);

        });
    }

    private Class<?> parseDevPanelType(Element devPanel) {
        if (devPanel == null) {
            throw new LightException("devPanelType not specify");
        }
        String devPanelClassName = devPanel.getTextContent();
        try {
            return Class.forName(devPanelClassName);
        } catch (ClassNotFoundException e) {
            throw new LightException(e);
        }
    }

    private MoCreator newBaseMoCreator(Element moCreator) {
        String attribute = moCreator.getAttribute(DEV_PANEL_TYPE);
        DevPanelType devPanelType;
        if (attribute == null) {
            devPanelType = DevPanelType.Fragment;
        } else {
            devPanelType = DevPanelType.valueOf(attribute);
        }
        BaseMoCreator baseMoCreator = new BaseMoCreator();
        baseMoCreator.setDevPanelType(devPanelType);
        return baseMoCreator;
    }

    private MoCreator parseMoCreator(Element moCreator) {
        String attribute = moCreator.getAttribute(CLASS);
        if (attribute == null) {
            throw new LightException("moCreator does not specify a class" + moCreator);
        }
        try {
            Class<?> creatorClass = Class.forName(attribute);
            Object o = creatorClass.newInstance();
            if (o instanceof MoCreator) {
                return (MoCreator) o;
            }
            throw new LightException("the bean's class is [" + o.getClass() + "], is not a instance of :" + MoCreator.class);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            throw new LightException(e);
        }


    }
}
